#!/usr/bin/python
#! -*- coding: utf8 -*-
import lorun
import os
import enum
import json

# 运行结果
RESULT_STR = [
    '通过',
    '格式错误',
    '运行超时',
    '内存超限',
    '答案错误',
    '运行时错误',
    '输出结果超限',
    '编译错误',
    '系统错误'
]

# RESULT_STR = [
#     'Accepted',
#     'Presentation Error',
#     'Time Limit Exceeded',
#     'Memory Limit Exceeded',
#     'Wrong Answer',
#     'Runtime Error',
#     'Output Limit Exceeded',
#     'Compile Error',
#     'System Error'
# ]


class ResultRunOne:
    def __init__(self, result, timeused, memoryused) -> None:
        self.result = result
        self.timeused = timeused
        self.memoryused = memoryused
        pass


class ResultRun:
    result = []  # 结果
    count = 0  # 题目数量
    pass_count= 0  # 通过数量
    language = 0  # 编译器
    isCompiled = 0  # 编译是否成功

    def __init__(self, result=[]) -> None:
        self.result = result
        self.isCompiled = 0
        pass

    def addResult(self, result_one):
        self.result.append(result_one)


class RESULT(enum.Enum):
    Accepted = 0
    Presentation_Error = 1
    Time_Limit_Exceeded = 2
    Memory_Limit_Exceeded = 3
    Wrong_Answer = 4
    Runtime_Error = 5
    Output_Limit_Exceeded = 6
    Compile_Error = 7
    System_Error = 8


def compileSrc(compiler_directive):  # 编译程序
    if compiler_directive == '' or compiler_directive == None:
        return True
    if os.system(compiler_directive) != 0:
        return False
    return True


def menu():  # 选项
    import argparse
    parser = argparse.ArgumentParser()
    parser.add_argument('--language', help='编程语言', required=False)
    parser.add_argument('--languageConfig', help='编程语言所在的文件夹', required=False, default='./language')
    parser.add_argument('--type', help='输出类型',
                        choices=['json', 'str'], default='str')
    parser.add_argument('--mode', help='判题模式',
                        choices=['entire', 'portion'], default='entire')
    parser.add_argument('--filePath', help='源代码文件所在路径', type=str, default='./')
    parser.add_argument('--fileSuffix', help='源代码文件后缀名', type=str, default='')
    parser.add_argument('--filePrefix', help='源代码文件前缀名', type=str, default='')       
    parser.add_argument('--file', help='源代码文件', type=str, default='')
    parser.add_argument('--testDir', help='测试文件夹', type=str, default='test')
    parser.add_argument('--codeResultDir', help='代码结果生成路径', type=str, default='./')
    parser.add_argument('--time', help='时间限制, 单位ms', type=int, default=60*1000)
    parser.add_argument('--memory', help='内存限制, 单位KB', type=int, default= 2000000)
    parser.add_argument('--deleteFile', help='删除源文件,true将在测试完后删除源文件, false不删除源文件',type=str, default='false', choices=['true','false'])
    args = parser.parse_args()
    return args


def runner(runner_directive: str, in_path, out_path, time, memory):  # 运行
    fin = open(in_path)
    ftemp = open('temp.out', 'w')
    ferr = open('err.txt', 'w')

    runcfg = {
        'args': runner_directive.split(),
        'fd_in': fin.fileno(),
        'fd_out': ftemp.fileno(),
        'fd_err': ferr.fileno(),
        'timelimit': time,  # in MS
        'memorylimit': memory,  # in KB
    }
    run_result = lorun.run(runcfg)
    ferr.close()
    ferr = open('err.txt')
    err = ferr.readlines()
    if err != []:  # 运行时错误
        run_result['result'] = RESULT.Runtime_Error.value
        ferr.close()
        fin.close()
        ftemp.close()
        return run_result
    ferr.close()
    fin.close()
    ftemp.close()
    if run_result['result'] == 0:
        ftemp = open('temp.out')
        fout = open(out_path)
        check_result = lorun.check(fout.fileno(), ftemp.fileno())
        if check_result != 0:
            run_result['result'] = check_result
        fout.close()
        ftemp.close()
    os.remove('temp.out')
    os.remove('err.txt')
    return run_result


def judge(language ,testdir: str, time, memory, compiler_directive, runner_directive, mode: str, type):  # 判题
    t = compileSrc(compiler_directive)
    if t == False:
        return f"{'{'} isCompiled:{RESULT.Compile_Error.value} {'}'}"
    # 获取testdir文件夹下的所有文件
    dir = os.listdir(testdir)
    count = int(len(dir) / 2)  # 文件数目
    result_str = ''
    result_run = ResultRun()
    result_run.count = count
    result_run.language = language
    for i in range(count):
        in_path = os.path.join(testdir, f'{i}.in')
        out_path = os.path.join(testdir, f'{i}.out')
        if os.path.isfile(in_path) and os.path.isfile(out_path):
            result_one = runner(runner_directive=runner_directive,
                                in_path=in_path,
                                out_path=out_path,
                                time=time,
                                memory=memory)

            result_run_one = ResultRunOne(
                result=result_one['result'], timeused=result_one['timeused'], memoryused=result_one['memoryused'])
            result_run.addResult(result_run_one.__dict__)

            result_one_tmp = f"测试结果{i}: {RESULT_STR[result_one['result']]} \t用时: {result_one['timeused']} \t内存占用: {result_one['memoryused']}"
            result_str += str(result_one_tmp)+'\n'

            if result_one['result'] != 0 and mode == 'portion':
                break
            if result_one['result'] == 0:
                result_run.pass_count = result_run.pass_count + 1

    if type == 'str':
        result_str+=f'语言：{result_run.language} 测试用例数量：{result_run.count} 通过数：{result_run.pass_count}'
        return result_str
    return json.dumps(result_run.__dict__)


def getFilePrefixName(file: str):  # 获取文件前缀名
    filenamelist = os.path.splitext(file)  # 文件
    filename = ''
    for i in range(len(filenamelist)-1):
        filename += filenamelist[i]
    return filename


def getFileSuffixName(file: str):  # 获取文件后缀名
    return os.path.splitext(file)[-1]


def getDirective(path, language):
    for file in os.listdir(path):
        # print(file)
        if file.endswith('Directive.json'):
            fp = open(os.path.join(path, file), 'r', encoding='utf-8')
            js = json.load(fp)
            fp.close()
            if js['language'] == language:
                return js

def defaultRun(file):  # 简易模式
    file_prefix = getFilePrefixName(file)  # 文件前缀名
    file_suffix = getFileSuffixName(file)  # 文件后缀名
    file = file_prefix+file_suffix
    directive = getDirective('./language/', file_suffix.replace('.',''))  # 获得命令模板

    # 填写命令模板
    if 'compiler' in directive.keys():
        compiler_directive = directive['compiler'].replace(
            'Main', file_prefix)  # 编译命令
    else:
        compiler_directive = None
    if 'runner' in directive.keys():
        runner_directive = directive['runner'].replace(
            'Main', file_prefix)  # 运行命令
    else:
        print('未找到运行命令，程序终止')
        exit(-1)
    result = judge(
        testdir='./test',
        language= file_suffix.replace('.',''),
        compiler_directive=compiler_directive,
        runner_directive=runner_directive,
        time=60*1000,
        memory=2000000,
        type='str',
        mode='entire'
    )
    print(result)
    if 'compilerName' in directive.keys()  and compiler_directive != None:
        os.remove(directive['compilerName'].replace('Main', file_prefix))
    

def menuRun():  # 标准模式
    args = menu()
    # print(args)
    # 源码文件名相关
    file = args.file  # 源文件名
    if file != '':
        file_prefix = getFilePrefixName(file)  # 文件前缀名
        file_suffix = getFileSuffixName(file)  # 文件后缀名
    else:
        file_prefix = args.filePrefix 
        file_suffix = args.fileSuffix
        if file_prefix == '' or file_suffix == '':
            print("请补全参数 [--file] 或 [--file_prefix --file_suffix]")
            exit(-1)
        file = file_prefix+file_suffix

    time = args.time  # 限时
    memory = args.memory  # 最大内存
    testdir = args.testDir
    mode = args.mode  # 判题模式
    type = args.type  # 输出类型
    language = args.language  # 编程语言
    deleteFile = args.deleteFile  # 删除
    languageConfig = args.languageConfig  # 编程语言所在的文件夹
    codeResultDir = args.codeResultDir  # 代码临时结果存放的文件夹
    os.chdir(codeResultDir)  # 进入codrResultDir文件夹

    directive = getDirective(languageConfig, language)  # 获得命令模板

    # 填写命令模板
    if 'compiler' in directive.keys():
        compiler_directive = directive['compiler'].replace(
            'Main', file_prefix)  # 编译命令
    else:
        compiler_directive = None
    if 'runner' in directive.keys():
        runner_directive = directive['runner'].replace(
            'Main', file_prefix)  # 运行命令
    else:
        print('未找到运行命令，程序终止')
        exit(-1)
    
    
    result = judge(testdir=testdir,
                    language= language,
                    compiler_directive=compiler_directive,
                    runner_directive=runner_directive,
                    time=time,
                    memory=memory,
                    type=type,
                    mode=mode)

    print(result)

    if 'compilerName' in directive.keys()  and compiler_directive != None:
        if os.path.exists(directive['compilerName'].replace('Main', file_prefix)):
            os.remove(directive['compilerName'].replace('Main', file_prefix))

    if 'true' == deleteFile:
        os.remove(file)

if __name__ == '__main__':
    import sys
    argv = sys.argv
    l = len(argv)
    if l < 2:
        print("请输入参数")
    elif l == 2:
        defaultRun(argv[len(argv)-1])
    else:
        menuRun()
    